/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#ifndef MC_BUILTINS_TUPLE_H
#define MC_BUILTINS_TUPLE_H

#include "mcpy/builtins/cast.h"
#include "mcpy/exception.h"
#include <tuple>
#include <type_traits>

namespace mcpy {

// ============================================================================
// 元组索引访问辅助函数
// ============================================================================

template <std::size_t Index, typename ResultT, typename... Args>
ResultT tuple_getitem(const std::tuple<Args...>& tuple) {
    static_assert(Index < sizeof...(Args), "Tuple index out of range");
    return mcpy::cast<ResultT>(std::get<Index>(tuple));
}

template <std::size_t Index, typename ResultT>
ResultT tuple_getitem(const mc::variants& tuple) {
    // 对于 mc::variants，直接访问索引
    int64_t size  = static_cast<int64_t>(tuple.size());
    int64_t index = static_cast<int64_t>(Index);
    if (index < 0) {
        index += size;
    }
    if (index < 0 || index >= size) {
        MC_THROW(mcpy::index_error, "tuple index out of range");
    }
    return mcpy::cast<ResultT>(tuple[static_cast<size_t>(index)]);
}

template <std::size_t Index, typename ResultT>
ResultT tuple_getitem(const mc::variant& variant) {
    MC_ASSERT_THROW(variant.is_array(), mcpy::type_error, "variant does not contain a tuple");
    return mcpy::tuple_getitem<Index, ResultT>(variant.get_array());
}

template <typename ResultT, typename... Args>
inline ResultT tuple_getitem(const std::tuple<Args...>& tuple, int64_t index) {
    constexpr std::size_t size = sizeof...(Args);
    if (index < 0) {
        index += static_cast<int64_t>(size);
    }
    if (index < 0 || index >= static_cast<int64_t>(size)) {
        MC_THROW(mcpy::index_error, "tuple index out of range");
    }

    mc::variant result;
    std::size_t current_index = 0;
    std::apply(
        [&](const auto&... elements) {
        (void)((current_index++ == static_cast<std::size_t>(index) ? (result = mc::variant(elements), true) : false) || ...);
    }, tuple);

    return mcpy::cast<ResultT>(result);
}

template <typename ResultT, typename V>
auto tuple_getitem(const V& tuple, int64_t index)
    -> std::enable_if_t<std::is_same_v<V, mc::variants> || mc::is_variant_v<V>, ResultT> {
    int64_t size = static_cast<int64_t>(tuple.size());
    if (index < 0) {
        index += size;
    }
    if (index < 0 || index >= size) {
        MC_THROW(mcpy::index_error, "tuple index out of range");
    }
    return mcpy::cast<ResultT>(tuple[static_cast<size_t>(index)]);
}

// ============================================================================
// 元组长度获取
// ============================================================================

/**
 * @brief 获取元组长度（编译时）
 */
template <typename... Args>
constexpr std::size_t tuple_len(const std::tuple<Args...>&) {
    return sizeof...(Args);
}

/**
 * @brief 获取元组长度（运行时，用于 mc::variants）
 */
inline std::size_t tuple_len(const mc::variants& tuple) {
    return tuple.size();
}

// ============================================================================
// 元组成员检测
// ============================================================================

/**
 * @brief 检查元素是否在元组中
 */
template <typename T, typename... Args>
bool tuple_contains(const std::tuple<Args...>& tuple, const T& item) {
    bool found = false;
    std::apply(
        [&](const auto&... elements) {
        // 只检查类型匹配的元素
        ((found = found || (std::is_same_v<std::decay_t<decltype(elements)>, std::decay_t<T>> && elements == item)), ...);
    },
        tuple);
    return found;
}

/**
 * @brief 检查元素是否在弱类型元组中
 */
inline bool tuple_contains(const mc::variants& tuple, const mc::variant& item) {
    for (const auto& element : tuple) {
        if (element == item) {
            return true;
        }
    }
    return false;
}

// ============================================================================
// 元组查找索引
// ============================================================================

/**
 * @brief 查找元素在元组中的索引
 *
 * @throws value_error 如果元素不在元组中
 */
template <typename T, typename... Args>
std::size_t tuple_index(const std::tuple<Args...>& tuple, const T& item) {
    std::size_t index = 0;
    bool        found = false;

    std::apply(
        [&](const auto&... elements) {
        ((void)(found || (
                             // 只比较类型匹配的元素
                             std::is_same_v<std::decay_t<decltype(elements)>, std::decay_t<T>> && elements == item
                                 ? (found = true, false)
                                 : (++index, false))),
         ...);
    },
        tuple);

    if (!found) {
        MC_THROW(mcpy::value_error, "tuple.index(x): x not in tuple");
    }

    return index;
}

/**
 * @brief 查找元素在弱类型元组中的索引
 */
inline std::size_t tuple_index(const mc::variants& tuple, const mc::variant& item) {
    for (std::size_t i = 0; i < tuple.size(); ++i) {
        if (tuple[i] == item) {
            return i;
        }
    }
    MC_THROW(mcpy::value_error, "tuple.index(x): x not in tuple");
}

// ============================================================================
// 元组计数
// ============================================================================

/**
 * @brief 计算元素在元组中出现的次数
 */
template <typename T, typename... Args>
std::size_t tuple_count(const std::tuple<Args...>& tuple, const T& item) {
    std::size_t count = 0;
    std::apply(
        [&](const auto&... elements) {
        // 只计数类型匹配的元素
        ((count += (std::is_same_v<std::decay_t<decltype(elements)>, std::decay_t<T>> && elements == item ? 1 : 0)), ...);
    },
        tuple);
    return count;
}

/**
 * @brief 计算元素在弱类型元组中出现的次数
 */
inline std::size_t tuple_count(const mc::variants& tuple, const mc::variant& item) {
    std::size_t count = 0;
    for (const auto& element : tuple) {
        if (element == item) {
            ++count;
        }
    }
    return count;
}

} // namespace mcpy

#endif // MC_BUILTINS_TUPLE_H
